
/*服务架构分为 单体架构 和 分布式架构 ,而 微服务 是在给分布式架构制定一个标准，进一步降低服务之间的耦合度，提供服务的独立性和灵活性。因此，可以认为 微服务 是一种经过良好架构设计的分布式架构方案*/


/*
SpringCloud是目前国内使用最广泛的一套微服务框架,集成了各种微服务功能组件用于解决分布式架构中存在的各种问题,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验。

SpringCloud底层是依赖于SpringBoot的,二者有严格的版本对应关系
SpringBoot框架用于构建单个项目(服务),SpringCloud框架为微服务体系开发中的架构问题提供了一整套的解决方案——服务注册与发现，服务消费，服务保护与熔断，网关，分布式调用追踪，分布式配置管理等
*/


/*简单说说RestTemplate底层是基于什么发送Http请求的？

RestTemplate 底层是基于 Java 的 `java.net.HttpURLConnection` 或 Apache HttpClient 发送 HTTP 请求的。

在 Java 11 之前，RestTemplate 默认使用的是 `java.net.HttpURLConnection` 来发送 HTTP 请求。`HttpURLConnection` 是 Java 标准库中的一个类，提供了基本的 HTTP 请求和响应功能。

从 Java 11 开始，RestTemplate 默认使用的是 Apache HttpClient 来发送 HTTP 请求。Apache HttpClient 是一个强大的 HTTP 客户端库，提供了丰富的功能和配置选项，可以更灵活地处理各种 HTTP 请求和响应。

RestTemplate 作为一个高级别的 HTTP 客户端，封装了底层的 HTTP 请求发送细节，提供了更简便的 API 供开发者使用。无论使用 `java.net.HttpURLConnection` 还是 Apache HttpClient，RestTemplate 都提供了统一的接口和方法来发送 GET、POST、PUT、DELETE 等 HTTP 请求，并处理响应结果。开发者可以根据需求选择合适的底层实现，或者自定义扩展 RestTemplate 的行为。*/


/*微服务中注册中心的作用是什么?

简答:注册中心服务器(如Eureka,Nacos,Zookeeper) 提供 服务注册、服务发现 等功能

完整:注册中心在微服务架构中扮演着重要的角色，其作用主要有以下几点：

1. 服务注册、服务发现：注册中心用于服务实例的注册和发现。每个微服务在启动时向注册中心注册自己的网络位置（IP地址和端口），以及提供的服务信息。其他微服务可以通过查询注册中心来发现可用的服务实例，从而实现服务间的通信和协作。

2. 服务健康检查：注册中心可以定期检查注册的服务实例是否可用和健康。通过心跳机制或其他方式，注册中心可以监测服务实例的状态，并从注册表中移除不可用或故障的实例。这样可以确保只有可用的服务实例被其他服务发现和调用。

3. 负载均衡：注册中心可以提供负载均衡的功能，通过在多个可用的服务实例之间分发请求，实现请求的负载均衡。通过注册中心提供的负载均衡算法，可以根据实际情况将请求分发到不同的服务实例上，以提高系统的性能和可伸缩性。

4. 服务配置管理：注册中心可以用于集中管理微服务的配置信息。微服务在启动时可以从注册中心获取所需的配置信息，例如数据库连接、缓存配置等。这样可以实现配置的集中管理和动态更新，避免了每个微服务都要维护自己的配置文件的麻烦。

5. 服务路由和网关：注册中心可以提供服务路由和网关的功能，将外部请求路由到适当的微服务实例上。通过配置路由规则和代理，注册中心可以充当服务的入口和访问控制的一层，提供更好的安全性和可扩展性。

总而言之，注册中心在微服务架构中起到了服务注册、发现、健康检查、负载均衡、配置管理等关键的作用，为微服务架构的弹性、可伸缩和可靠性提供了基础设施支持。*/


/*@LoadBalanced 允许RestTemplate实例去Eureka注册中心服务器中拉取服务实例列表，并且实现负载均衡(基于Ribbon负载均衡器)
Ribbon底层基于 LoadBalancerInterceptor负载均衡拦截器 实现负载均衡*/

/*
    Nacos与eureka异同点

  - Nacos与eureka的共同点
  - 都支持服务注册和服务拉取
  - 都支持服务提供者心跳方式做健康检测

- Nacos与Eureka的区别
  - Nacos支持服务端主动检测提供者状态：临时实例采用心跳模式，非临时实例采用主动检测模式
  - 临时实例心跳不正常会被剔除，非临时实例则不会被剔除
  - Nacos支持服务列表变更的消息推送模式，服务列表更新更及时
  - Nacos集群默认采用AP方式，当集群中存在非临时实例时，采用CP模式；Eureka采用AP方式

*/


/*配置中心 实现 集中配置,方便管理*/


/**
 * ## 配置热更新
 * <p>
 * 我们最终的目的，是修改nacos中的配置后，微服务中无需重启即可让配置生效，也就是**配置热更新**。
 * 要实现配置热更新，可以使用两种方式：
 * <p>
 * 方式一
 * 在@Value注入的变量所在类上添加注解@RefreshScope：
 *
 * @RefreshScope public class UserController{
 * @Value("${pattern.dataformat}")//从Nacos配置中心中的配置中获取的配置参数 private String dataformat
 * }
 * <p>
 * 方式二
 * 使用@ConfigurationProperties注解代替@Value注解。
 * 在user-service服务中，添加一个类，读取patterrn.dateformat属性：
 * <p>
 * package cn.itcast.user.config;
 * import lombok.Data;
 * import org.springframework.boot.context.properties.ConfigurationProperties;
 * import org.springframework.stereotype.Component;
 * @Component
 * @Data
 * @ConfigurationProperties(prefix = "pattern")
 * public class PatternProperties {
 * private String dateformat;
 * }
 * 在UserController中使用这个类代替@Value：
 * @Slf4j
 * @RestController
 * @RequestMapping("/user") public class UserController {
 * @Autowired private UserService userService;
 * @Autowired private PatternProperties patternProperties;
 * @GetMapping("now") public String now(){
 * return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat());
 * }
 * 这种配置类的方式默认能够'配置热更新'无需@RefreshScope注解
 * <p>
 * <p>
 * 注意:数据库连接等框架自带的配置内容是不能热更新的,服务会监听到配置中心配置文件变化但是,不会与数据库重新建立连接
 */

/*配置文件中同属性参数优先级
 * 远程具体环境配置文件application-profile(test/dev/prod...).yml > 远程通用环境配置文件application.yml > 本地VM参数配置-Dserver.port=8082 -Dspring.cloud.nacos.discovery.cluster-name=SH > 本地配置文件*/

/*Feign底层与RestTemplate底层一样都是使用的JDK自带的HttpUrlConnection

实际使用的是OpenFeign,它在Feign的基础上提供了SpringMVC的注解,让代码编写更方便

Feign用来代替RestTemplate实现微服务与微服务之间的调用,如果需要发http请求去调用第三方url还是需要使用RestTemplate

与RestTemplate一样,Feign底层负载均衡也是基于Ribbon负载均衡器*/



/*
                        Feign使用过程用的注意事项

1.实际开发中当需要通过?name=zhangsan这种形式传参调用其他服务时,被调服务的Controller中直接
@GetMapping("/query")
public User queryByName(String name){
    System.out.println(name);
    return null;
}
即可,无需加@RequestParam,只需要?后面的键值对键名同形参名即可自动对应上
但是如果使用Feign进行远程调用,注意@FeignClient接口中的函数中形参前必须加上@RequestParam才能接收到参数?name=zhangsan,不加name将为null
@FeignClient("userservice")
public interface UserFeign {
    @GetMapping("/user/query")
    public User queryByName(@RequestParam("name") String name);
}

2.与之类似的另一种情形是,前端通过form表单提交数据,Controller中接收函数的形参是一个实体类对象如User user,但不是Json的形式,没有@RequestBody注解,而是?a=..&b=..&c=..的形式,a,b,c..对应于User的成员变量,此时与上面的情况类似,Controller中实体类形参前不加注解形参user是可以接收到请求参数的,但是如果只用Feign发起远程调用,@FeignClient接口中的函数实体类形参前必须加上@SpringQueryMap注解user形参才能接收到请求参数

3.当@FeignClient接口中的函数返回值为集合时,泛型一定要指明,否则集合内容物会变成LinkedHashMap,遍历集合时会出现类型转换错误异常
*/


/*
网关geteway的作用:客户端访问微服务统一的入口
具体而言,服务路由,负载均衡,身份认证,权限校验,请求限流
网关常见技术实现有SpringCloudGateway和Zuul,SpringCloudGateway有更好的性能,现在是主流

网关搭建步骤：
1. 创建项目，引入nacos服务发现和gateway依赖
2. 配置application.yml，包括服务基本信息、nacos地址、路由

路由配置包括：
1. 路由id：路由的唯一标示
2. 路由目标（uri）：路由的目标地址，http代表固定地址，lb代表根据服务名负载均衡,常用后者
3. 路由断言（predicates）：判断路由的规则,指定什么格式的url地址才往路由目标指定的服务转发请求
4. 路由过滤器（filters）：对请求或响应做处理

Route Predicate Factory 路由断言工厂的作用:从配置文件中读取用户定义的断言规则,解析成路由判断的条件
Spring中提供了11种基本的断言工厂
名称    说明                   示例
After   是某个时间点后的请求 - After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before  是某个时间点之前的请求 - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between 是某两个时间点之前的请求 - Between=2037-01-20T17:42:47.789-07:00[America/Denver],  2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie  请求必须包含某些 - cookie- Cookie=chocolate, ch.p
Header  请求必须包含某些 - header- Header=X-Request-Id, \d+
Host    请求必须是访问某个 - host（域名）-  Host=**.somehost.org,**.anotherhost.org
Method  请求方式必须是指定方式  - Method=GET,POST
Path    请求路径必须符合指定规则 - Path=/red/{segment},/blue/**
Query   请求参数必须包含指定参数 - Query=name, Jack或者-  Query=name
RemoteAddr 请求者的ip必须是指定范围 - RemoteAddr=192.168.1.1/24
Weight  权重处理
*/

/*
GatewayFilter是网关中提供的一种过滤器，可以对进入网关的请求和微服务返回的响应做处理
Spring提供了31种不同的网关过滤器工厂
配置在某条路由规则下的过滤器只对当前路由的请求生效
如果要对所有的路由都添加网关过滤器，则可以将过滤器配置写到default-filters下
 */

/*
全局过滤器 GlobalFilter

全局过滤器的作用也是处理一切进入网关的请求和微服务响应，与GatewayFilter的作用一样。
区别在于GatewayFilter通过配置定义，处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。
定义方式是实现GlobalFilter接口。
 */

/*
过滤器执行顺序

请求进入网关会碰到三类过滤器：当前路由的过滤器、DefaultFilter、GlobalFilter

请求路由后，会将当前路由过滤器和DefaultFilter、GlobalFilter，合并到一个过滤器链（集合）中，排序后依次执行每个过滤器：

排序的规则是什么呢？

- 每一个过滤器都必须指定一个int类型的order值，order值越小，优先级越高，执行顺序越靠前。
- GlobalFilter通过实现Ordered接口，或者添加@Order注解来指定order值，由我们自己指定
- 路由过滤器和defaultFilter的order由Spring指定，默认是按照声明顺序从1递增,先声明的order小,先执行
- 当过滤器的order值一样时，会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。
 */

/*
跨域：域名不一致就是跨域，主要包括：

- 域名不同： www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com
- 域名相同，端口不同：localhost:8080和localhost8081

跨域问题：浏览器禁止请求的发起者与服务端发生跨域ajax请求，请求被浏览器拦截的问题

解决方案：CORS(浏览器发起options请求询问服务器是否允许请求跨域)，这个以前应该学习过，这里不再赘述了。不知道的小伙伴可以查看https://www.ruanyifeng.com/blog/2016/04/cors.html

### 解决跨域问题
在gateway服务的application.yml文件中，添加下面的配置：
spring:
  cloud:
    gateway:
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]': # 拦截哪些请求 /** 表示一切请求
            allowedOrigins: # 允许哪些网站的跨域请求
              - "http://localhost:8090"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期,单位秒,有效期内浏览器将不再发起询问服务器是否允许跨域,而是直接放行
 */

/*
网关限流过滤器

限流：对应用服务器的请求做限制，避免因过多请求而导致服务器过载甚至宕机。限流算法常见的包括3种：
计数器算法，又包括窗口计数器算法、滑动窗口计数器算法
漏桶算法(Leaky Bucket)
令牌桶算法（Token Bucket）

限流过滤器-计数器算法
固定窗口计数器算法概念如下：
将时间划分为多个窗口；
在每个窗口内每有一次请求就将计数器加一，当时间到达下一个窗口时，计数器重置。
如果计数器超过了限制数量，则本窗口内所有的请求都被丢弃。

限流过滤器-漏桶算法
漏桶算法说明：
将每个请求视作"水滴"放入"漏桶"进行存储；
"漏桶"以固定速率向外"漏"出请求来执行，如果"漏桶"空了则停止"漏水”；
如果"漏桶"满了则多余的"水滴"会被直接丢弃。

限流过滤器-令牌桶算法
漏桶算法说明：
以固定的速率生成令牌，存入令牌桶中，如果令牌桶满了以后，多余令牌丢弃
请求进入后，必须先尝试从桶中获取令牌，获取到令牌后才可以被处理
如果令牌桶中没有令牌，则请求等待或丢弃

三者中令牌桶较好,可控性强,令牌生成速率可以控制访问速率,桶的容量可以控制瞬时并发量
 */